package com.leyou.search.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.leyou.common.utils.JsonUtils;
import com.leyou.common.utils.NumberUtils;
import com.leyou.common.vo.PageResult;
import com.leyou.item.pojo.*;
import com.leyou.search.client.BrandClient;
import com.leyou.search.client.CategoryClient;
import com.leyou.search.client.GoodsClient;
import com.leyou.search.client.SpecClient;
import com.leyou.search.pojo.Goods;
import com.leyou.search.pojo.SearchRequest;
import com.leyou.search.pojo.SearchResult;
import com.leyou.search.repository.GoodsRepository;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchService {

    @Autowired
    public GoodsRepository goodsRepository;

    /**
     *
     */
    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private BrandClient brandClient;

    @Autowired
    private GoodsClient goodsClient;

    @Autowired
    private SpecClient specClient;

    @Autowired
    private ElasticsearchTemplate template;

    public Goods buildGoods(Spu spu) {

        Goods goods = new Goods();
        goods.setId(spu.getId());
        goods.setBrandId(spu.getBrandId());
        goods.setCid1(spu.getCid1());
        goods.setCid2(spu.getCid2());
        goods.setCid3(spu.getCid3());
        goods.setCreateTime(spu.getCreateTime());
        goods.setSubTitle(spu.getSubTitle());

        Brand brand = brandClient.queryBrandById(spu.getBrandId());
        List<Category> categories = categoryClient.queryCategoryByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
        List<String> names = categories.stream().map(Category::getName).collect(Collectors.toList());
        String all = spu.getTitle() + StringUtils.join(names, " ") + brand.getName();
        goods.setAll(all);
        List<Sku> skuList = goodsClient.querySkuBySpuId(spu.getId());
        List<Long> prices = skuList.stream().map(Sku::getPrice).collect(Collectors.toList());
        goods.setPrice(prices);
        List<Map<String,Object>> skus = new ArrayList<>();
        for (Sku sku : skuList) {
            Map<String,Object> map = new HashMap<>();
            map.put("id", sku.getId());
            map.put("title", sku.getTitle());
            map.put("price", sku.getPrice());
            map.put("image",StringUtils.substringBefore(sku.getImages(),","));
            skus.add(map);
        }
        goods.setSkus(JsonUtils.serialize(skus));
        List<SpecParam> specParams = specClient.querySpecParams(null, spu.getCid3(), true);
        SpuDetail detail = goodsClient.querySpuDetailById(spu.getId());
        Map<Long, String> genericMap = JsonUtils.parseMap(detail.getGenericSpec(), Long.class, String.class);
        Map<Long,List<String>> specMap = JsonUtils.nativeRead(detail.getSpecialSpec(), new TypeReference<Map<Long, List<String>>>() {
        });
        Map<String, Object> specs = new HashMap<>();
        for (SpecParam param : specParams) {
            if (param.getGeneric()) {
                String value = genericMap.get(param.getId());
                if (param.getNumeric()) {
                    value = chooseSegment(value, param);
                }
                specs.put(param.getName(), value);
            } else {
                specs.put(param.getName(), specMap.get(param.getId()));
            }
        }
        goods.setSpecs(specs);
        return goods;
    }
    private String chooseSegment(String value, SpecParam p) {
        double val = NumberUtils.toDouble(value);
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = NumberUtils.toDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if(segs.length == 2){
                end = NumberUtils.toDouble(segs[1]);
            }
            // 判断是否在范围内
            if(val >= begin && val < end){
                if(segs.length == 1){
                    result = segs[0] + p.getUnit() + "以上";
                }else if(begin == 0){
                    result = segs[1] + p.getUnit() + "以下";
                }else{
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    public PageResult<Goods> search(SearchRequest request) {
        String key = request.getKey();
        if(StringUtils.isBlank(key)){
            // 如果用户没搜索条件，我们可以给默认的，或者返回null
            return null;
        }

        Integer page = request.getPage() - 1;// page 从0开始
        Integer size = request.getSize();
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","skus","subTitle"}, null));
        QueryBuilder query = buildBasicQueryWithFilter(request);
        queryBuilder.withQuery(query);
        queryBuilder.withPageable(PageRequest.of(page, size));
        String categoryAggName = "category";
        String brandAggName = "brand";
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid3"));
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));
        AggregatedPage<Goods> result = template.queryForPage(queryBuilder.build(), Goods.class);
        long total = result.getTotalElements();
        long totalPages = result.getTotalPages();
        Aggregations aggs = result.getAggregations();
        List<Category> categories = parseCategory(aggs.get(categoryAggName));
        List<Brand> brands = parseBrand(aggs.get(brandAggName));
        List<Map<String, Object>> specs = null;
        if (categories != null && categories.size() == 1) {

            specs = getSpecs(categories.get(0).getId(), query);

        }
        return new SearchResult(total,totalPages,result.getContent(),categories,brands,specs);
    }

    private QueryBuilder buildBasicQueryWithFilter(SearchRequest request) {
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        Map<String,String> filter = request.getFilter();
        queryBuilder.must(QueryBuilders.matchQuery("all", request.getKey()));
        for (Map.Entry<String, String> entry : filter.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (key != "cid3" && key != "brandId") {
                key = "specs." + key + ".keyword";
            }
            queryBuilder.filter(QueryBuilders.termQuery(key, value));
        }
        return queryBuilder;
    }

    private List<Map<String,Object>> getSpecs(Long id, QueryBuilder query) {
        List<SpecParam> params = specClient.querySpecParams(null, id, true);
        List<Map<String, Object>> specs = new ArrayList<>();
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        for (SpecParam param : params) {
            String name = param.getName();
            queryBuilder.addAggregation(AggregationBuilders.terms(name).field("specs." + name + ".keyword"));
        }
        AggregatedPage<Goods> result = template.queryForPage(queryBuilder.build(), Goods.class);
        Aggregations aggs = result.getAggregations();
        for (SpecParam param : params) {
            String name = param.getName();
            StringTerms terms = aggs.get(name);
            List<String> options = terms.getBuckets().stream()
                    .map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
            Map<String, Object> map = new HashMap<>();
            map.put("k", name);
            map.put("options", options);
            specs.add(map);
        }

        return specs;
    }

    private List<Brand> parseBrand(LongTerms terms) {
        List<Long> bids = new ArrayList<>();
        for (LongTerms.Bucket bucket : terms.getBuckets()) {
            bids.add(bucket.getKeyAsNumber().longValue());
        }

        return brandClient.queryBrandsByIds(bids);
    }

    private List<Category> parseCategory(LongTerms terms) {
        List<Long> cids = new ArrayList<>();
        for (LongTerms.Bucket bucket : terms.getBuckets()) {
            cids.add(bucket.getKeyAsNumber().longValue());
        }
        return categoryClient.queryCategoryByIds(cids);
    }
}
